Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Medicine administration UI #10289

Open
wants to merge 23 commits into
base: develop
Choose a base branch
from
Open

Conversation

amjithtitus09
Copy link
Contributor

@amjithtitus09 amjithtitus09 commented Jan 30, 2025

Proposed Changes

  • Includes Patient Updates tab response view (Reported by @aravindm4 )
  • Includes fix for Structured responses not being filtered by Encounter while pre-filling forms (Reported by @aravindm4 )

@ohcnetwork/care-fe-code-reviewers

Merge Checklist

  • Add specs that demonstrate bug / test a new feature.
  • Update product documentation.
  • Ensure that UI text is kept in I18n files.
  • Prep screenshot or demo video for changelog entry, and attach it to issue.
  • Request for Peer Reviews
  • Completion of QA

Summary by CodeRabbit

  • Localization

    • Added new English translations for various medical and administrative terms.
  • New Features

    • Introduced medication administration management functionality.
    • Added new components for medication request and administration tracking.
    • Enhanced patient details and questionnaire response tracking.
    • Implemented new API endpoints for medication administration retrieval and submission.
  • Improvements

    • Updated patient and encounter-specific data retrieval.
    • Improved medication request and administration interfaces.
    • Modified button default variant.
  • Bug Fixes

    • Refined medication dosage formatting.
    • Updated component prop handling for more robust data management.

@amjithtitus09 amjithtitus09 requested a review from a team as a code owner January 30, 2025 09:40
@amjithtitus09 amjithtitus09 requested a review from bodhish January 30, 2025 09:40
Copy link
Contributor

coderabbitai bot commented Jan 30, 2025

Walkthrough

This pull request introduces comprehensive enhancements to medication administration functionality across multiple components. The changes include new components for managing medication administration, updates to localization files, modifications to existing patient and encounter-related components, and the addition of new API endpoints and type definitions. The implementation focuses on providing a more robust and user-friendly interface for healthcare professionals to track, administer, and manage medication requests.

Changes

File Change Summary
public/locale/en.json Added multiple localization keys for medication administration and status tracking
src/Utils/request/api.tsx Added new medication administration endpoint for listing patient medications
src/components/Medicine/* Introduced new components for medication administration, including AdministrationTab, MedicineAdminDialog, MedicineAdminForm, MedicineAdminSheet, and utility functions
src/components/Questionnaire/QuestionTypes/* Updated question components to include encounterId for more context-specific data retrieval
src/types/emr/medicationAdministration/* Added new interfaces and API definitions for medication administration

Suggested labels

needs review

Poem

🐰 Hop, hop, hooray! Meds are here to stay!
With dialogs and forms so bright,
Tracking pills from morning to night
A rabbit's guide to healing's art
Where technology and care can start! 🩺💊


📜 Recent review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between bd6edc4 and 37a29f5.

📒 Files selected for processing (1)
  • public/locale/en.json (11 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • public/locale/en.json
⏰ Context from checks skipped due to timeout of 90000ms (3)
  • GitHub Check: cypress-run (1)
  • GitHub Check: OSSAR-Scan
  • GitHub Check: OSSAR-Scan

Thank you for using CodeRabbit. We offer it for free to the OSS community and would appreciate your support in helping us grow. If you find it useful, would you consider giving us a shout-out on your favorite social media?

❤️ Share
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Generate unit testing code for this file.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai generate unit testing code for this file.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and generate unit testing code.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate docstrings to generate docstrings for this PR. (Beta)
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

Copy link

netlify bot commented Jan 30, 2025

Deploy Preview for care-ohc ready!

Name Link
🔨 Latest commit a1773bf
🔍 Latest deploy log https://app.netlify.com/sites/care-ohc/deploys/679b7c33c114140008d0f16a
😎 Deploy Preview https://deploy-preview-10289--care-ohc.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.

To edit notification comments on pull requests, go to your Netlify site configuration.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 11

🔭 Outside diff range comments (3)
src/components/Questionnaire/QuestionTypes/SymptomQuestion.tsx (2)

Line range hint 291-297: Add encounterId to the query key for proper cache invalidation.

The query won't automatically refetch when the encounter changes because encounterId is not included in the query key. This could lead to stale data being displayed.

Apply this diff to ensure proper cache invalidation:

  const { data: patientSymptoms } = useQuery({
-   queryKey: ["symptoms", patientId],
+   queryKey: ["symptoms", patientId, encounterId],
    queryFn: query(symptomApi.listSymptoms, {
      pathParams: { patientId },
      queryParams: {
        encounter: encounterId,
        limit: 100,
      },
    }),
  });

Line range hint 300-311: Add missing dependencies to useEffect.

The effect's dependency array is missing updateQuestionnaireResponseCB and questionnaireResponse.question_id. This could lead to stale closures and potentially cause updates to be lost.

Apply this diff to fix the dependency array:

  useEffect(() => {
    if (patientSymptoms?.results) {
      updateQuestionnaireResponseCB(
        [
          {
            type: "symptom",
            value: patientSymptoms.results.map(convertToSymptomRequest),
          },
        ],
        questionnaireResponse.question_id,
      );
    }
-  }, [patientSymptoms]);
+  }, [patientSymptoms, updateQuestionnaireResponseCB, questionnaireResponse.question_id]);
src/components/Patient/PatientDetailsTab/index.tsx (1)

Remove redundant patientId prop

The patientId prop is redundant with the existing id prop. The codebase consistently uses id as the primary identifier, and when patientId is needed, it's derived from either id or patientData.id. To maintain consistency and avoid confusion, use only the id prop.

🔗 Analysis chain

Line range hint 13-17: Verify patientId prop usage.

The patientId prop has been added to PatientProps, but it seems redundant with the existing id prop.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Check if patientId and id are used differently
rg -A 5 "patientId.*=.*props" src/
rg -A 5 "id.*=.*props" src/

Length of output: 17668

🧹 Nitpick comments (14)
src/components/ui/button.tsx (1)

45-47: Consider documenting the default variant change.

Since this is a breaking change that could affect the appearance of buttons across the application, consider:

  1. Adding a comment explaining why "primary" is now the default variant
  2. Updating component documentation if it exists
  3. Communicating this change to other developers
src/components/Questionnaire/QuestionTypes/DiagnosisQuestion.tsx (1)

87-87: Consider adding error handling for invalid encounter IDs.

While the implementation is correct, consider adding validation or error handling for cases where the encounterId might be invalid or empty.

 export function DiagnosisQuestion({
   encounterId,
   patientId,
   questionnaireResponse,
   updateQuestionnaireResponseCB,
   disabled,
 }: DiagnosisQuestionProps) {
+  if (!encounterId?.trim()) {
+    console.warn('Invalid or empty encounter ID provided');
+  }
   const diagnoses =
     (questionnaireResponse.values?.[0]?.value as DiagnosisRequest[]) || [];

   const { data: patientDiagnoses } = useQuery({

Also applies to: 101-101

src/components/Questionnaire/QuestionTypes/MedicationRequestQuestion.tsx (1)

81-81: LGTM! Consider documenting the encounter-specific behavior.

The implementation correctly adds encounter context to medication requests. Consider adding a comment explaining how encounter filtering affects medication requests visibility.

 interface MedicationRequestQuestionProps {
   patientId: string;
   questionnaireResponse: QuestionnaireResponse;
   updateQuestionnaireResponseCB: (
     values: ResponseValue[],
     questionId: string,
     note?: string,
   ) => void;
   disabled?: boolean;
+  /** 
+   * Encounter ID used to filter medication requests.
+   * When provided, only shows medications associated with this encounter.
+   */
   encounterId: string;
 }

Also applies to: 89-89, 99-99

src/components/Medicine/MedicationAdministration/AdministrationTab.tsx (3)

43-48: Ensure extensibility for alternate time partitions
The hard-coded time slots could be made configurable if the application needs to adapt to different shift schedules or time block conventions in the future. Storing them in a separate configuration (or retrieving from an endpoint) might allow more flexibility.


65-85: Consider extracting visible slot calculation logic
The memoized calculation of visible time slots is rather involved and could be extracted into a custom hook or separate utility. This approach will improve readability and testability.


549-557: Validate safety checks before administering
The “Administer” button is only shown when medication.status === "active" in the current time slot. Ensure that all necessary distribution, ingestion, or cross-verification checks are performed prior to pediatric or high-risk medication administration.

src/components/Medicine/MedicationAdministration/utils.ts (1)

26-33: Simplify the dose property assignment.

The code has repetitive access to the dose quantity object, which can be simplified.

Apply this diff to reduce repetition:

-     dose: medication.dosage_instruction[0]?.dose_and_rate?.dose_quantity && {
-       value:
-         medication.dosage_instruction[0]?.dose_and_rate?.dose_quantity?.value,
-       unit: medication.dosage_instruction[0]?.dose_and_rate?.dose_quantity
-         ?.unit,
-       code: medication.dosage_instruction[0]?.dose_and_rate?.dose_quantity
-         ?.unit,
+     dose: (() => {
+       const doseQuantity = medication.dosage_instruction?.[0]?.dose_and_rate?.dose_quantity;
+       return doseQuantity && {
+         value: doseQuantity.value,
+         unit: doseQuantity.unit,
+         code: doseQuantity.unit,
+       };
+     })(),
src/types/emr/medicationAdministration/medicationAdministration.ts (1)

56-96: Consider extracting common properties into a shared interface.

The MedicationAdministrationRequest and MedicationAdministrationRead interfaces share many common properties. Consider creating a base interface to reduce duplication:

interface BaseMedicationAdministration {
  status: MedicationAdministrationStatus;
  status_reason?: Code;
  medication: Code;
  occurrence_period_start: string;
  occurrence_period_end?: string;
  recorded?: string;
  note?: string;
  dosage?: {
    text?: string;
    site?: Code;
    route?: Code;
    method?: Code;
    dose?: DosageQuantity;
    rate?: Quantity;
  };
}

export interface MedicationAdministrationRequest extends BaseMedicationAdministration {
  id?: string;
  encounter: string;
  request: string;
}

export interface MedicationAdministrationRead extends BaseMedicationAdministration {
  id: string;
  encounter: string;
  request: string;
}
src/components/Medicine/MedicationRequestTable/MedicineItem.tsx (1)

23-74: Enhance accessibility with ARIA labels and better contrast.

Consider the following improvements:

  1. Add aria-label to the grid container
  2. Use semantic HTML elements for the medication name (h3 is good)
  3. Ensure text contrast meets WCAG guidelines, especially for text-muted-foreground
-    <div className="space-y-1">
+    <div className="space-y-1" role="article" aria-label={`Medication details for ${medication.medication?.display}`}>
src/components/Medicine/MedicationAdministration/MedicineAdminDialog.tsx (1)

43-46: Prevent potential race conditions in useEffect.

The current useEffect implementation might cause race conditions if initialRequest changes rapidly. Consider adding a cleanup function:

  React.useEffect(() => {
+   let isActive = true;
-   setAdministrationRequest(initialRequest);
+   if (isActive) {
+     setAdministrationRequest(initialRequest);
+   }
+   return () => {
+     isActive = false;
+   };
  }, [initialRequest]);
src/components/Medicine/MedicationAdministration/MedicineAdminForm.tsx (1)

181-195: Consider consolidating date/time picker logic.

The start and end time picker implementations share similar logic. Consider extracting this into a reusable component to reduce code duplication.

Also applies to: 197-220

src/components/Medicine/MedicationAdministration/MedicineAdminSheet.tsx (1)

242-248: Consider adding loading state to submit button.

The submit button should show a loading state during mutation to prevent double submissions.

 <Button
   type="submit"
   className="bg-[#006D4C] hover:bg-[#006D4C]/90"
-  disabled={selectedMedicines.size === 0}
+  disabled={selectedMedicines.size === 0 || isSubmitting}
 >
-  {t("administer_medicines")} ({selectedMedicines.size})
+  {isSubmitting ? t("submitting") : `${t("administer_medicines")} (${selectedMedicines.size})`}
 </Button>
src/types/emr/medicationRequest.ts (1)

95-96: Track technical debt: Remove code property after backend update.

The TODO comment indicates this is a temporary change. Create a follow-up ticket to remove this property once the backend administration dosage is updated from Quantity to DosageQuantity.

Would you like me to create a tracking issue for removing this temporary code once the backend is updated?

src/components/Medicine/MedicationRequestTable/index.tsx (1)

115-219: Excellent UI/UX improvements with comprehensive features.

The UI implementation includes:

  • Clear tab separation between prescriptions and administration
  • Responsive search with instant feedback
  • Proper handling of loading and empty states
  • Horizontal scrolling for wide content

Consider adding keyboard shortcuts for common actions like searching and clearing the search input.

 <input
   type="text"
   placeholder="Search medications..."
   value={searchQuery}
   onChange={(e) => setSearchQuery(e.target.value)}
+  onKeyDown={(e) => {
+    if (e.key === 'Escape') {
+      setSearchQuery('');
+    }
+  }}
   className="flex-1 bg-transparent text-sm outline-none placeholder:text-muted-foreground"
 />
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 50cb157 and 866b730.

📒 Files selected for processing (27)
  • public/locale/en.json (11 hunks)
  • src/Utils/request/api.tsx (2 hunks)
  • src/components/Facility/ConsultationDetails/QuestionnaireResponsesList.tsx (2 hunks)
  • src/components/Medicine/MedicationAdministration/AdministrationTab.tsx (1 hunks)
  • src/components/Medicine/MedicationAdministration/MedicineAdminDialog.tsx (1 hunks)
  • src/components/Medicine/MedicationAdministration/MedicineAdminForm.tsx (1 hunks)
  • src/components/Medicine/MedicationAdministration/MedicineAdminSheet.tsx (1 hunks)
  • src/components/Medicine/MedicationAdministration/utils.ts (1 hunks)
  • src/components/Medicine/MedicationRequestTable/MedicineItem.tsx (1 hunks)
  • src/components/Medicine/MedicationRequestTable/MedicineList.tsx (1 hunks)
  • src/components/Medicine/MedicationRequestTable/index.tsx (3 hunks)
  • src/components/Medicine/MedicationsTable.tsx (1 hunks)
  • src/components/Medicine/MultiValueSetSelect.tsx (1 hunks)
  • src/components/Medicine/utils.ts (1 hunks)
  • src/components/Patient/PatientDetailsTab/index.tsx (3 hunks)
  • src/components/Patient/PatientHome.tsx (1 hunks)
  • src/components/Questionnaire/QuestionTypes/AllergyQuestion.tsx (3 hunks)
  • src/components/Questionnaire/QuestionTypes/DiagnosisQuestion.tsx (3 hunks)
  • src/components/Questionnaire/QuestionTypes/MedicationRequestQuestion.tsx (2 hunks)
  • src/components/Questionnaire/QuestionTypes/MedicationStatementQuestion.tsx (3 hunks)
  • src/components/Questionnaire/QuestionTypes/QuestionInput.tsx (1 hunks)
  • src/components/Questionnaire/QuestionTypes/SymptomQuestion.tsx (3 hunks)
  • src/components/ui/button.tsx (1 hunks)
  • src/pages/Encounters/tabs/EncounterUpdatesTab.tsx (1 hunks)
  • src/types/emr/medicationAdministration/medicationAdministration.ts (3 hunks)
  • src/types/emr/medicationAdministration/medicationAdministrationApi.ts (1 hunks)
  • src/types/emr/medicationRequest.ts (1 hunks)
🧰 Additional context used
🪛 GitHub Actions: Cypress Tests
src/components/Patient/PatientDetailsTab/index.tsx

[error] 35-35: TypeScript error: Cannot find name 'Updates'

⏰ Context from checks skipped due to timeout of 90000ms (2)
  • GitHub Check: OSSAR-Scan
  • GitHub Check: Cloudflare Pages: care-fe
🔇 Additional comments (30)
src/components/Questionnaire/QuestionTypes/SymptomQuestion.tsx (2)

49-49: LGTM! The encounterId prop is correctly typed.

The addition of the required encounterId property aligns with the PR objective to filter symptoms by encounter.


285-285: LGTM! The encounterId prop is correctly destructured.

The prop is properly added to the component's parameter list and matches the interface definition.

src/components/ui/button.tsx (1)

45-45: Verify the impact of changing the default variant.

Changing the default variant from "default" to "primary" will affect all instances where the Button component is used without an explicit variant prop. This could lead to unexpected visual changes across the application.

Let's verify the Button component usage in the codebase:

✅ Verification successful

Change appears safe due to explicit variant usage pattern

The codebase demonstrates a consistent pattern where button variants are explicitly specified rather than relying on default values. This significantly reduces the risk of unintended visual changes from modifying the default variant.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Find all instances of Button component usage without explicit variant prop

# Search for Button component imports
echo "Button component imports:"
rg "import.*Button.*from.*components/ui/button"

# Search for Button usage without variant prop
# Note: This might have false positives but helps identify potential impacts
echo -e "\nButton usage without variant prop:"
ast-grep --pattern '<Button $$$>'

# Search for Button usage with explicit variants
echo -e "\nButton usage with explicit variants:"
ast-grep --pattern '<Button variant="$_" $$$>'

Length of output: 65794

src/components/Questionnaire/QuestionTypes/QuestionInput.tsx (2)

118-136: LGTM! Implementation aligns with PR objectives.

The conditional rendering of medication components based on encounterId effectively addresses the bug where structured responses weren't being filtered by Encounter. The implementation is clean and handles the absence of encounterId gracefully.


Line range hint 138-174: Verify if appointment type should check for encounterId.

While the implementation for allergy, symptom, diagnosis, and encounter types is consistent and well-structured, the appointment type doesn't follow the same pattern of checking for encounterId. This might be intentional, but it's worth verifying.

Let's verify if other appointment-related components in the codebase require encounterId:

src/components/Questionnaire/QuestionTypes/DiagnosisQuestion.tsx (1)

48-48: LGTM! Props interface updated correctly.

The encounterId property is properly typed as string in the interface.

src/components/Questionnaire/QuestionTypes/MedicationStatementQuestion.tsx (1)

63-63: LGTM! Implementation matches the pattern.

The changes correctly integrate the encounter context into the medication statement queries. The implementation is consistent with other components.

Also applies to: 94-94, 114-114

src/components/Questionnaire/QuestionTypes/AllergyQuestion.tsx (1)

63-63: Verify allergy data filtering behavior.

The implementation looks correct, but since allergies are critical for patient safety, we should verify that filtering by encounter doesn't hide important allergy information from other encounters.

Also applies to: 120-120, 131-131

src/components/Medicine/MedicationAdministration/AdministrationTab.tsx (2)

152-164: Validate correct handling of older administration records
The logic accumulates the most recent occurrence date per medication. Confirm that older records aren’t mistakenly excluded from other computations if they need to be displayed or tracked.


313-314: Verify status transitions for discontinuation
When discontinuing a medication, it transitions to "ended". Verify that additional business rules (e.g., reason for discontinuation) are captured if required.

src/types/emr/medicationAdministration/medicationAdministrationApi.ts (1)

9-20: Confirm return types of upsert endpoint
The upsertMedicationAdministration definition indicates the response type as an array of MedicationAdministrationRequest objects. Verify it matches the backend's actual response (often newly updated or created resources return read objects or references).

src/components/Medicine/MedicationRequestTable/MedicineList.tsx (2)

9-14: LGTM! Well-structured props interface.

The MedicineListProps interface is well-defined with clear types and proper optional property marking.


16-40: LGTM! Clean and efficient implementation.

The component follows React best practices:

  • Proper key usage in map function
  • Controlled component pattern for checkboxes
  • Clear separation of concerns between selection and display
src/components/Medicine/MedicationAdministration/utils.ts (1)

18-19: Consider timezone handling in date formatting.

Both start and end times are set to the same moment using new Date(). This might need to account for timezone differences and duration.

Would you like me to provide an implementation that handles timezones properly?

src/components/Patient/PatientDetailsTab/index.tsx (1)

70-70: ⚠️ Potential issue

Fix TypeScript error: Updates component reference.

The pipeline is failing because the Updates component is still referenced in patientTabs but has been removed.

Apply this diff to fix the error:

  {
    route: "updates",
-   component: Updates,
+   component: QuestionnaireResponsesList,
  },

Likely invalid or redundant comment.

src/pages/Encounters/tabs/EncounterUpdatesTab.tsx (1)

49-52: LGTM! Proper prop passing to QuestionnaireResponsesList.

The addition of the patientId prop aligns with the PR objectives and maintains consistency with other components in the file.

src/types/emr/medicationAdministration/medicationAdministration.ts (1)

46-46: LGTM! Type consistency improvement.

The change from Quantity to DosageQuantity aligns the type with medication request definitions, ensuring consistent typing across the codebase.

src/components/Medicine/MedicationsTable.tsx (1)

Line range hint 22-31: LGTM! Good function extraction.

Making getFrequencyDisplay exportable improves code reusability while maintaining its functionality.

src/components/Medicine/MultiValueSetSelect.tsx (1)

58-58: LGTM! Proper propagation of disabled state.

The addition of the disabled prop to the Button component correctly propagates the disabled state from parent to child, ensuring consistent UI behavior.

src/components/Facility/ConsultationDetails/QuestionnaireResponsesList.tsx (2)

17-18: LGTM! Props interface updated correctly.

The changes to make encounter optional and add required patientId improve component reusability while maintaining type safety.


128-141: LGTM! API parameters updated appropriately.

The changes to use patientId directly and make encounter conditional align with the interface changes and PR objectives.

src/components/Medicine/MedicationAdministration/MedicineAdminForm.tsx (1)

45-48: Consider adding validation for lastAdministeredDate.

The component accepts lastAdministeredDate as an optional string but doesn't validate its format before using it with new Date(). This could lead to runtime errors with invalid dates.

src/components/Patient/PatientHome.tsx (1)

139-139: LGTM! Consistent prop passing.

The addition of the patientId prop enables child components to access the patient identifier, which is necessary for the medication administration functionality.

src/Utils/request/api.tsx (1)

657-663: LGTM! Well-structured API endpoint.

The new medication administration endpoint follows the established API patterns:

  • RESTful path structure
  • Consistent with other patient-related endpoints
  • Proper typing with PaginatedResponse
src/components/Medicine/MedicationRequestTable/index.tsx (2)

21-50: Well-structured EmptyState component with good UX.

The EmptyState component is well-designed with:

  • Clear prop interface
  • Conditional messaging based on search state
  • Consistent styling with the UI theme

65-101: Clean implementation of medication status management.

Good implementation of medication status handling:

  • Separate queries for active and stopped medications
  • Efficient state management with showStopped
  • Clear separation of concerns in data fetching
public/locale/en.json (4)

752-752: LGTM! Comprehensive status keys for medication administration workflow.

The added status keys provide clear and concise states for tracking medication administration:

  • "draft": For in-progress administration records
  • "ended": For completed administration cycles
  • "in_progress": For active administration
  • "not_done": For skipped or cancelled administration
  • "prescribed": For medications that are prescribed but not yet administered
  • "taken": For successfully administered medications

Also applies to: 902-902, 1071-1071, 1386-1386, 1585-1585, 1985-1985


759-759: LGTM! Clear action and search labels.

The added keys provide clear labels for medication-related actions:

  • "edit_administration": For editing medication administration records
  • "search_medicine": For medicine search functionality

Also applies to: 1810-1810


1137-1137: LGTM! Appropriate messages for various states.

The added keys provide clear messages for:

  • Past time administration confirmation
  • Empty states for diagnoses and symptoms

Also applies to: 1322-1322, 1373-1373


Line range hint 752-1985: Verify consistency in terminology.

Ensure consistent usage of terms across the application:

  1. "medicine" vs "medication"
  2. "administration" vs "administered"
✅ Verification successful

Terminology usage is consistent and follows healthcare standards

The codebase demonstrates intentional and consistent terminology usage:

  • Backend/API layer uses formal "medication" terminology aligned with healthcare standards (FHIR)
  • Frontend/UI layer uses more user-friendly "medicine" terminology for better user experience
  • "Administration" related terms are used consistently across all layers
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Search for inconsistent terminology usage
echo "Checking for terminology consistency..."
rg -i "medic(ine|ation)" 
rg -i "administ(ration|ered|er)"

Length of output: 71229

Comment on lines +203 to +214
if (administrations?.results?.length) {
const sortedAdmins = [...administrations.results].sort(
(a, b) =>
new Date(b.occurrence_period_start).getTime() -
new Date(a.occurrence_period_start).getTime(),
);

if (!lastModifiedDate) {
setLastModifiedDate(new Date(sortedAdmins[0].occurrence_period_start));
}
}
}, [administrations]);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Potential stale lastModifiedDate issue
Since lastModifiedDate is only set when no previous date exists, any new administration record after the component mounts won’t update this state (line 210). If tracking the latest modification time is important, consider removing the if (!lastModifiedDate) condition.

- if (!lastModifiedDate) {
+ // Always re-check if the new admin is newer
  setLastModifiedDate(new Date(sortedAdmins[0].occurrence_period_start));
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
if (administrations?.results?.length) {
const sortedAdmins = [...administrations.results].sort(
(a, b) =>
new Date(b.occurrence_period_start).getTime() -
new Date(a.occurrence_period_start).getTime(),
);
if (!lastModifiedDate) {
setLastModifiedDate(new Date(sortedAdmins[0].occurrence_period_start));
}
}
}, [administrations]);
if (administrations?.results?.length) {
const sortedAdmins = [...administrations.results].sort(
(a, b) =>
new Date(b.occurrence_period_start).getTime() -
new Date(a.occurrence_period_start).getTime(),
);
// Always re-check if the new admin is newer
setLastModifiedDate(new Date(sortedAdmins[0].occurrence_period_start));
}
}, [administrations]);

Comment on lines +16 to +19
path: "/api/v1/patient/{patientId}/medication/administration/upsert/",
method: HttpMethod.POST,
TRes: Type<MedicationAdministrationRequest[]>,
},
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Support partial failures
Consider whether the upsert endpoint should handle partial errors in case some medication administration requests fail, while others succeed. Returning a more detailed response with success/failure statuses may improve transparency.

Comment on lines +8 to +13
if (dose_range) {
return `${dose_range.low.value}${dose_range.low.unit.display} - ${dose_range.high.value}${dose_range.high.unit.display}`;
} else if (dose_quantity) {
return `${dose_quantity.value} ${dose_quantity.unit.display}`;
}

const { dose_quantity } = instruction.dose_and_rate;
if (!dose_quantity?.value || !dose_quantity.unit) return "";

return `${dose_quantity.value} ${dose_quantity.unit.display}`;
return "";
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Handle missing or invalid dosage data
The logic gracefully checks for dose_range or dose_quantity. However, if dose_range.low.value or .high.value is absent or invalid, this may cause runtime errors. A more robust guard or fallback could help prevent potential crashes.

  if (dose_range) {
+   if (!dose_range.low?.value || !dose_range.low?.unit || !dose_range.high?.value || !dose_range.high?.unit) {
+     return "";
+   }
    return `${dose_range.low.value}${dose_range.low.unit.display} - ${dose_range.high.value}${dose_range.high.unit.display}`;
  } else if (dose_quantity) {
+   if (!dose_quantity.value || !dose_quantity.unit) {
+     return "";
+   }
    return `${dose_quantity.value} ${dose_quantity.unit.display}`;
  }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
if (dose_range) {
return `${dose_range.low.value}${dose_range.low.unit.display} - ${dose_range.high.value}${dose_range.high.unit.display}`;
} else if (dose_quantity) {
return `${dose_quantity.value} ${dose_quantity.unit.display}`;
}
const { dose_quantity } = instruction.dose_and_rate;
if (!dose_quantity?.value || !dose_quantity.unit) return "";
return `${dose_quantity.value} ${dose_quantity.unit.display}`;
return "";
if (dose_range) {
if (!dose_range.low?.value || !dose_range.low?.unit || !dose_range.high?.value || !dose_range.high?.unit) {
return "";
}
return `${dose_range.low.value}${dose_range.low.unit.display} - ${dose_range.high.value}${dose_range.high.unit.display}`;
} else if (dose_quantity) {
if (!dose_quantity.value || !dose_quantity.unit) {
return "";
}
return `${dose_quantity.value} ${dose_quantity.unit.display}`;
}
return "";

Comment on lines +23 to +25
site: medication.dosage_instruction[0]?.site,
route: medication.dosage_instruction[0]?.route,
method: medication.dosage_instruction[0]?.method,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Add bounds checking for dosage_instruction array access.

The code directly accesses dosage_instruction[0] without checking if the array is empty, which could lead to runtime errors.

Apply this diff to add safety checks:

    dosage: {
-     site: medication.dosage_instruction[0]?.site,
-     route: medication.dosage_instruction[0]?.route,
-     method: medication.dosage_instruction[0]?.method,
+     site: medication.dosage_instruction?.[0]?.site,
+     route: medication.dosage_instruction?.[0]?.route,
+     method: medication.dosage_instruction?.[0]?.method,
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
site: medication.dosage_instruction[0]?.site,
route: medication.dosage_instruction[0]?.route,
method: medication.dosage_instruction[0]?.method,
site: medication.dosage_instruction?.[0]?.site,
route: medication.dosage_instruction?.[0]?.route,
method: medication.dosage_instruction?.[0]?.method,

Comment on lines +44 to +45
{formatDosage(medication.dosage_instruction[0])}
</p>
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Add null check for dosage_instruction array access.

Direct access to medication.dosage_instruction[0] could throw if the array is empty. Consider using optional chaining:

-            {formatDosage(medication.dosage_instruction[0])}
+            {formatDosage(medication.dosage_instruction?.[0])}
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
{formatDosage(medication.dosage_instruction[0])}
</p>
{formatDosage(medication.dosage_instruction?.[0])}
</p>

});
};

console.log("patientId", patientId);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Remove debug console.log statement.

Remove the debug console.log statement before merging to production.

-  console.log("patientId", patientId);

Comment on lines +113 to +136
<div className="space-y-2">
<Label>{t("status")}</Label>
<Select
value={administrationRequest.status}
onValueChange={(value: MedicationAdministrationStatus) =>
onChange({ ...administrationRequest, status: value })
}
disabled={
!!administrationRequest.id &&
administrationRequest.status !== "in_progress"
}
>
<SelectTrigger>
<SelectValue placeholder={t("select_status")} />
</SelectTrigger>
<SelectContent>
{MEDICATION_ADMINISTRATION_STATUS.map((status) => (
<SelectItem key={status} value={status}>
{t(status)}
</SelectItem>
))}
</SelectContent>
</Select>
</div>
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Consider adding status transition validation.

The status selection logic should validate whether the transition from one status to another is valid according to the medication administration workflow.

 <Select
   value={administrationRequest.status}
   onValueChange={(value: MedicationAdministrationStatus) =>
-    onChange({ ...administrationRequest, status: value })
+    onChange({
+      ...administrationRequest,
+      status: validateStatusTransition(administrationRequest.status, value)
+        ? value
+        : administrationRequest.status,
+    })
   }
   disabled={
     !!administrationRequest.id &&
     administrationRequest.status !== "in_progress"
   }
 >

Committable suggestion skipped: line range outside the PR's diff.

Comment on lines 159 to 165
const now = new Date().toISOString().slice(0, 16);
onChange({
...administrationRequest,
occurrence_period_start: now,
occurrence_period_end: now,
});
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Potential timezone issue with date handling.

Using toISOString().slice(0, 16) might lead to timezone-related issues. Consider using a more robust date formatting approach.

-const now = new Date().toISOString().slice(0, 16);
+const now = new Date().toISOString();

Committable suggestion skipped: line range outside the PR's diff.

Comment on lines +61 to +71
const { mutate: upsertAdministrations } = useMutation({
mutationFn: mutate(
medicationAdministrationApi.upsertMedicationAdministration,
{
pathParams: { patientId: patientId },
},
),
onSuccess: () => {
onOpenChange(false);
},
});
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Consider adding error handling for mutation failures.

The mutation success case is handled, but there's no error handling. Consider adding error handling and user feedback.

 const { mutate: upsertAdministrations } = useMutation({
   mutationFn: mutate(
     medicationAdministrationApi.upsertMedicationAdministration,
     {
       pathParams: { patientId: patientId },
     },
   ),
   onSuccess: () => {
     onOpenChange(false);
   },
+  onError: (error) => {
+    // Handle error and show user feedback
+    console.error('Failed to update administrations:', error);
+    // Consider adding a toast notification here
+  },
 });
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const { mutate: upsertAdministrations } = useMutation({
mutationFn: mutate(
medicationAdministrationApi.upsertMedicationAdministration,
{
pathParams: { patientId: patientId },
},
),
onSuccess: () => {
onOpenChange(false);
},
});
const { mutate: upsertAdministrations } = useMutation({
mutationFn: mutate(
medicationAdministrationApi.upsertMedicationAdministration,
{
pathParams: { patientId: patientId },
},
),
onSuccess: () => {
onOpenChange(false);
},
onError: (error) => {
// Handle error and show user feedback
console.error('Failed to update administrations:', error);
// Consider adding a toast notification here
},
});

Comment on lines +116 to +130
const handleSubmit = async (e: React.FormEvent<HTMLFormElement>) => {
e.preventDefault();

const administrations = Array.from(selectedMedicines).map((id) => {
return administrationRequests[id];
});

upsertAdministrations({
datapoints: administrations,
});

onOpenChange(false);
setSelectedMedicines(new Set());
setAdministrationRequests({});
};
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Add validation before submission.

The submit handler should validate all administration requests before submission to ensure data integrity.

 const handleSubmit = async (e: React.FormEvent<HTMLFormElement>) => {
   e.preventDefault();

   const administrations = Array.from(selectedMedicines).map((id) => {
     return administrationRequests[id];
   });

+  // Validate all administrations before submission
+  const invalidAdministrations = administrations.filter(
+    (admin) => !isValidAdministrationRequest(admin)
+  );
+  if (invalidAdministrations.length > 0) {
+    // Handle invalid administrations
+    return;
+  }

   upsertAdministrations({
     datapoints: administrations,
   });

Committable suggestion skipped: line range outside the PR's diff.

Copy link

cloudflare-workers-and-pages bot commented Jan 30, 2025

Deploying care-fe with  Cloudflare Pages  Cloudflare Pages

Latest commit: a1773bf
Status: ✅  Deploy successful!
Preview URL: https://57f68a93.care-fe.pages.dev
Branch Preview URL: https://medicine-administration-ui.care-fe.pages.dev

View logs

Copy link

cypress bot commented Jan 30, 2025

CARE    Run #4491

Run Properties:  status check passed Passed #4491  •  git commit a1773bf005: Medicine administration UI
Project CARE
Branch Review medicine-administration-ui
Run status status check passed Passed #4491
Run duration 05m 29s
Commit git commit a1773bf005: Medicine administration UI
Committer Amjith Titus
View all properties for this run ↗︎

Test results
Tests that failed  Failures 0
Tests that were flaky  Flaky 0
Tests that did not run due to a developer annotating a test with .skip  Pending 0
Tests that did not run due to a failure in a mocha hook  Skipped 0
Tests that passed  Passing 9
View all changes introduced in this branch ↗︎

@amjithtitus09
Copy link
Contributor Author

@nihal467 Needs testing

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

♻️ Duplicate comments (1)
src/components/Medicine/MedicationAdministration/MedicineAdminDialog.tsx (1)

48-58: ⚠️ Potential issue

Add error handling to mutation.

The mutation lacks error handling. Consider adding onError callback and showing a user-friendly error message.

  const { mutate: upsertAdministration, isPending } = useMutation({
    mutationFn: mutate(
      medicationAdministrationApi.upsertMedicationAdministration,
      {
        pathParams: { patientId: patientId },
      },
    ),
    onSuccess: () => {
      onOpenChange(false);
    },
+   onError: (error) => {
+     // Show error toast/notification
+     console.error('Failed to save medication administration:', error);
+   },
  });
🧹 Nitpick comments (3)
src/components/Medicine/MedicationAdministration/MedicineAdminForm.tsx (2)

45-48: Evaluate initialization of "isPastTime" logic.

When administrationRequest.occurrence_period_start !== administrationRequest.occurrence_period_end or administrationRequest.id is truthy, the form defaults to a "past time" scenario. This logic may conflate normal usage scenarios in which the user only wants to start and end at different times in the future. Consider making this condition more explicit if the goal is specifically to handle backdated administration events.


149-179: Ensure robust logic for scheduling past administrations.

While toggling "isPastTime," the code sets both start and end times to the current timestamp if "no" is selected. If a user previously entered manual times, these are overwritten. Consider prompting the user to confirm or preserving the prior input if it was unintentional.

src/components/Medicine/MedicationAdministration/MedicineAdminDialog.tsx (1)

68-68: Consider adding max-height for better mobile UX.

The dialog content might benefit from a max-height constraint with overflow scrolling for better mobile experience.

-      <DialogContent className="sm:max-w-[525px]">
+      <DialogContent className="sm:max-w-[525px] max-h-[90vh] overflow-y-auto">
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 73a28f5 and bd6edc4.

📒 Files selected for processing (2)
  • src/components/Medicine/MedicationAdministration/MedicineAdminDialog.tsx (1 hunks)
  • src/components/Medicine/MedicationAdministration/MedicineAdminForm.tsx (1 hunks)
⏰ Context from checks skipped due to timeout of 90000ms (3)
  • GitHub Check: cypress-run (1)
  • GitHub Check: OSSAR-Scan
  • GitHub Check: OSSAR-Scan
🔇 Additional comments (6)
src/components/Medicine/MedicationAdministration/MedicineAdminForm.tsx (4)

59-76: Confirm time zone handling with formatDistanceToNow.

The formatDistanceToNow function uses system locale settings. In multi-timezone environments, users may see inaccurate intervals if the times are stored or interpreted in UTC. Double-check that this display aligns with your workflow and that you account for any potential mismatch between local time and stored times.


113-136: Revisit status transition validation.

This component changes the status with no guard to ensure valid transitions (e.g., "in_progress" → "completed"). Consider restricting disallowed transitions to adhere to medication administration workflow rules, or else you risk inconsistent states.


181-220: Validate "end_time" is not earlier than "start_time".

When the user updates the end time, there is no check preventing an earlier end from preceding the previously set start. This could cause logical inconsistencies in administration periods.

Would you like me to provide a script to test for potential invalid administration periods throughout the code?


37-223: Overall structure and readability are good.

The component is well-organized and leverages form elements effectively. Use of i18n, date-fns, and typed properties for medication administration offers clarity and maintainability.

src/components/Medicine/MedicationAdministration/MedicineAdminDialog.tsx (2)

1-30: LGTM! Well-structured imports and type definitions.

The imports are well-organized, and the Props interface is properly typed with all necessary fields.


40-46: Consider handling state updates during pending mutations.

There's a potential race condition where the state could be updated via useEffect while a mutation is in progress. Consider disabling the effect or preserving the pending state when isPending is true.

React.useEffect(() => {
  if (!isPending) {
    setAdministrationRequest(initialRequest);
  }
}, [initialRequest, isPending]);

Comment on lines +60 to +64
const handleSubmit = () => {
upsertAdministration({
datapoints: [administrationRequest], // Send as single-item array for upsert
});
};
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Add form validation before submission.

Consider adding validation checks before submitting the form to ensure all required fields are filled and data is in the correct format.

  const handleSubmit = () => {
+   // Validate required fields
+   if (!administrationRequest.dosage || !administrationRequest.administeredAt) {
+     // Show validation error
+     return;
+   }
    upsertAdministration({
      datapoints: [administrationRequest],
    });
  };
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const handleSubmit = () => {
upsertAdministration({
datapoints: [administrationRequest], // Send as single-item array for upsert
});
};
const handleSubmit = () => {
// Validate required fields
if (!administrationRequest.dosage || !administrationRequest.administeredAt) {
// Show validation error
return;
}
upsertAdministration({
datapoints: [administrationRequest], // Send as single-item array for upsert
});
};

@github-actions github-actions bot added the merge conflict pull requests with merge conflict label Jan 30, 2025
Copy link

👋 Hi, @amjithtitus09,
Conflicts have been detected against the base branch. Please rebase your branch against the base branch.

@github-actions github-actions bot removed the merge conflict pull requests with merge conflict label Jan 30, 2025
@nihal467
Copy link
Member

nihal467 commented Jan 30, 2025

Things changed

  • Show detail updates in the patient updates section on the patient details page
  • Don't prefill the last encounter data in the new encounter
  • Medicine Administration have been added

@nihal467
Copy link
Member

nihal467 commented Jan 30, 2025

image

  • adding notes to the already added medicine administration are throwing 400 error

image
image

  • some of the administration data are only visible when we scroll back

image

image

  • restrict users from selecting the date of administration before the start time of their encounters
  • administration should be restricted for users from selecting future time

image

  • hide the discontinue button for already discontinued medicine

Enhancement

image

  • freeze the column to medicine, if users are scrolling to the right and freeze the time frame row, if the users are scrolling to the bottom

image

  • Keep the height on the row, consistent, and add a collapsed view for medicine administration beyond a count

image

  • restrict the left button to scroll beyond the medicine start date

non related issue

image

  • duplicate medication addition shouldn't be allowed

@nihal467 nihal467 added this to the Stable Staging Release milestone Jan 30, 2025
@nihal467 nihal467 added the P1 breaking issue or vital feature label Jan 31, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
P1 breaking issue or vital feature test failed
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants